package com.lordjoe.collectiveintelligence;

import java.util.*;
import java.io.*;

/**
 * com.lordjoe.collectiveintelligence.FlightOptimization
 *
 * @author Steve Lewis
 * @date Mar 24, 2009
 */
public class FlightOptimization
{
    public static FlightOptimization[] EMPTY_ARRAY = {};
    public static Class THIS_CLASS = FlightOptimization.class;
    public static final Random RND = new Random();

    private final TravelItem[] m_Items = {
            new  TravelItem("Seymour","BOS"),
            new  TravelItem("Franny","DAL"),
            new  TravelItem("Zooey","CAK"),
            new  TravelItem("Walt","MIA"),
            new  TravelItem("Seymour","BOS") ,
            new  TravelItem("Buddy","ORD") ,
            new  TravelItem("Les","OMA") ,
    };

    private Map<String,Flight[]> m_FlightsFrom =
            new HashMap<String,Flight[]>();

    private Map<String,Flight[]> m_FlightsTo =
          new HashMap<String,Flight[]>();

    public FlightOptimization()
    {
       handleFlightData();
    }

    protected void handleFlightData()
    {
        try {
            LineNumberReader rdr = new LineNumberReader(new FileReader("Schedule.txt"));
            String line = rdr.readLine();
            while (line != null) {
                Flight flt = new Flight(line);
                registerFlight(flt);
                line = rdr.readLine();
            }
        }
        catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    protected void registerFlight(Flight f)  {
        String start = f.getStartAirport();
        Flight[] flts = getFlightsFrom(start);
        Flight[] latest  = null;
        if(flts == null) {
            Flight[] item = { f };
            latest = item;
            f.setDailyFromIndex(0);
        }
        else {
            List<Flight> holder = new ArrayList<Flight>(Arrays.asList(flts));
            f.setDailyFromIndex(flts.length);
            holder.add(f);
            latest = new Flight[holder.size()];
            holder.toArray(latest);
        }
        m_FlightsFrom.put(start,latest);

        String end = f.getEndAirport();
         flts = getFlightsTo(end);
        latest  = null;
        if(flts == null) {
            Flight[] item = { f };
            latest = item;
            f.setDailyToIndex(0);
        }
        else {
            List<Flight> holder = new ArrayList<Flight>(Arrays.asList(flts));
            f.setDailyToIndex(flts.length);
            holder.add(f);
            latest = new Flight[holder.size()];
            holder.toArray(latest);
        }
        m_FlightsTo.put(end,latest);

    }

    public Flight[] getFlightsFrom(String airport)
    {
        return m_FlightsFrom.get(airport);
    }

    public Flight[] getFlightsTo(String airport)
    {
        return m_FlightsTo.get(airport);
    }

    public TravelItem[] getItems()
    {
        return m_Items;
    }

    public FlightItinery randomItinery(ICostFunction<FlightItinery> cost)
    {
        TravelItem[] items = getItems();
        ItineryItem[] its = new ItineryItem[items.length];
        for (int i = 0; i < items.length; i++) {
            its[i] = randomItineryItem(items[i]);

        }
        return new  FlightItinery(its,this,cost);
    }


    public ItineryItem randomItineryItem(TravelItem item)
    {
        String Airport =  item.getAirport();
        Flight[] flightsFrom = getFlightsFrom(Airport);
        Flight[] flightsTo = getFlightsTo(Airport);

        if(flightsFrom == null || flightsTo == null)
            throw new UnsupportedOperationException("Fix This"); // ToDo

        Flight flt = flightsFrom[RND.nextInt(flightsFrom.length)];
        Flight retflt = flightsTo[RND.nextInt(flightsTo.length)];

        return new ItineryItem(item.getPerson(),flt,retflt);
    }

    public Flight selectNewArrival(Flight arrival)
    {
        String city = arrival.getStartAirport();
        int fromIndex = arrival.getDailyFromIndex();
        Flight[] items = getFlightsFrom(city);
        if(fromIndex == 0)
            return items[1];
        if(fromIndex == items.length - 1)
            return items[items.length - 2];
        boolean b = RND.nextBoolean();
        if(b)
            return items[fromIndex + 1];
        else
            return items[fromIndex - 1];

    }


    public Flight selectNewReturn(Flight arrival)
    {
        String city = arrival.getEndAirport();
        int fromIndex = arrival.getDailyToIndex();
        Flight[] items = getFlightsTo(city);
        if(fromIndex == 0)
            return items[1];
        if(fromIndex == items.length - 1)
            return items[items.length - 2];
        boolean b = RND.nextBoolean();
        if(b)
            return items[fromIndex + 1];
        else
            return items[fromIndex - 1];

    }



    public static void main(String[] args)
    {
       FlightOptimization opt = new FlightOptimization();
        FlightItinery best = OptimizationUtilities.randomSearch(opt, FlightItineryCost.INSTANCE);
        System.out.print(best.toString());

        FlightItinery hbest = OptimizationUtilities.hillClimb(opt, FlightItineryCost.INSTANCE);
        System.out.print(hbest.toString());

        FlightItinery sbest = OptimizationUtilities.simulatedAnnealing(opt, FlightItineryCost.INSTANCE);
        System.out.print(sbest.toString());

        FlightItinery gbest = OptimizationUtilities.geneticEvolution(opt, FlightItineryCost.INSTANCE);
        System.out.print(sbest.toString());

    }
}
